package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;


    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private KafkaService kafkaService;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    /**
     * 采用微信JavaSDK进行微信登录
     *
     * @param code
     * @return
     */
    //@Override
    //public Map<String, String> weiXinLogin(String code) {
    //    try {
    //        //1.根据code调用微信SDK获取用户会话信息-得到微信用户唯一标识(OpenID)（微信端用户标识不变）-大家无法调用（没有在开发者列表）
    //        WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
    //        if (sessionInfo != null) {
    //            String openid = sessionInfo.getOpenid();
    //            //2.根据openId查询用户记录  TODO 固定写死OpenID odo3j4q2KskkbbW-krfE-cAxUnzU
    //            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
    //            queryWrapper.eq(UserInfo::getWxOpenId, openid);
    //            UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
    //
    //            //2.1 根据OpenId没有得到用户记录  新增用户记录 且 采用MQ异步初始化账户（余额）信息
    //            if (userInfo == null) {
    //                userInfo = new UserInfo();
    //                userInfo.setWxOpenId(openid);
    //                userInfo.setNickname("听友" + IdUtil.getSnowflake());
    //                userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
    //                userInfoMapper.insert(userInfo);
    //                //TODO 发送异步MQ消息，通知账户微服务初始化当前用户账户余额信息
    //            }
    //            //2.2 根据OpenID获取到用户记录，
    //
    //            //3.为登录微信用户生成令牌，将令牌存入Redis中
    //            String token = IdUtil.fastSimpleUUID();
    //            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
    //            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
    //            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
    //
    //
    //            //4.响应令牌
    //            Map<String, String> mapResult = new HashMap<>();
    //            mapResult.put("token", token);
    //            return mapResult;
    //        }
    //        return null;
    //    } catch (WxErrorException e) {
    //        log.error("微信登录异常：{}", e);
    //        throw new RuntimeException(e);
    //    }
    //}


    /**
     * 采用微信JavaSDK进行微信登录
     *
     * @param code
     * @return
     */
    @Override
    public Map<String, String> weiXinLogin(String code) {
        try {
            //1.根据code调用微信SDK获取用户会话信息-得到微信用户唯一标识(OpenID)（微信端用户标识不变）-大家无法调用（没有在开发者列表）
            //WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            //if (sessionInfo != null) {
            //    String openid = sessionInfo.getOpenid();
            //String openid = "odo3j4q2KskkbbW-krfE-cAxUnzU"; //晴天用户OpenId
            String openid = "odo3j4q2KskkbbW-krfE-cAxUnzU-aasdfwrw";
            //2.根据openId查询用户记录  TODO 固定写死OpenID odo3j4q2KskkbbW-krfE-cAxUnzU
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getWxOpenId, openid);
            UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);

            //2.1 根据OpenId没有得到用户记录  新增用户记录 且 采用MQ异步初始化账户（余额）信息
            if (userInfo == null) {
                userInfo = new UserInfo();
                userInfo.setWxOpenId(openid);
                userInfo.setNickname("听友" + IdUtil.getSnowflake().nextId());
                userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
                userInfoMapper.insert(userInfo);
                //TODO 发送异步MQ消息，通知账户微服务初始化当前用户账户余额信息
                kafkaService.sendMessage(KafkaConstant.QUEUE_USER_REGISTER, userInfo.getId().toString());
            }
            //2.2 根据OpenID获取到用户记录，

            //3.为登录微信用户生成令牌，将令牌存入Redis中
            String token = IdUtil.fastSimpleUUID();
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);


            //4.响应令牌
            Map<String, String> mapResult = new HashMap<>();
            mapResult.put("token", token);
            return mapResult;
        } catch (Exception e) {
            log.error("微信登录异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    @GuiGuCache(prefix = "userInfo:")
    public UserInfoVo getUserInfoVoByUserId(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        //数据脱密
        return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
    }

    /**
     * 更新用户信息
     *
     * @param userInfoVo
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo) {
        //1.先删除缓存
        redisTemplate.delete("userInfo:" + userInfoVo.getId());
        //2.先更新数据库
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userInfoVo.getId());
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        userInfo.setNickname(userInfoVo.getNickname());
        userInfoMapper.updateById(userInfo);
        //3.睡眠一段时间，确保此刻并发读的线程执行完毕
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //4.再次删除缓存
        redisTemplate.delete("userInfo:" + userInfoVo.getId());
    }


    /**
     * 获取专辑声音列表某页中，用户对于声音付费情况
     *
     * @param userId      用户ID
     * @param albumId     专辑ID
     * @param trackIdList 需要判断购买请求声音ID集合（从用户查询专辑声音分页）
     * @return
     */
    @Override
    public Map<Long, Integer> userIsPaidTrackList(Long userId, Long albumId, List<Long> trackIdList) {
        //1.根据用户ID+专辑ID查询专辑购买记录 如果有记录，将trackIdList购买情况返回1
        LambdaQueryWrapper<UserPaidAlbum> userPaidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getUserId, userId);
        userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(userPaidAlbumLambdaQueryWrapper);
        if (count > 0) {
            //用户购买该专辑
            Map<Long, Integer> map = new HashMap<>();
            for (Long trackId : trackIdList) {
                //将购买结果：已购买
                map.put(trackId, 1);
            }
            return map;
        }

        //2.根据用户ID+声音列表查询声音购买集合记录，判断哪些声音被购买，哪些没有被购买
        LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getUserId, userId);
        userPaidTrackLambdaQueryWrapper.in(UserPaidTrack::getTrackId, trackIdList);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(userPaidTrackLambdaQueryWrapper);
        if (CollectionUtil.isEmpty(userPaidTrackList)) {
            //2.1 专辑当前页包含声音列表用户一个都没购买
            Map<Long, Integer> map = new HashMap<>();
            for (Long trackId : trackIdList) {
                //将购买结果：未购买
                map.put(trackId, 0);
            }
            return map;
        }

        //2.2 用户有购买声音 判断哪些是被购买，哪些未被购买
        //2.2.1 得到用户已购买声音ID
        List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        //2.2.2 遍历待检测声音ID列表 判断已购买声音记录中是否包含声音ID
        Map<Long, Integer> map = new HashMap<>();
        for (Long trackId : trackIdList) {
            if (userPaidTrackIdList.contains(trackId)) {
                map.put(trackId, 1);
            } else {
                map.put(trackId, 0);
            }
        }
        return map;
    }


    /**
     * 判断用户是否购买过指定专辑
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long userId, Long albumId) {
        LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidAlbum::getUserId, userId);
        queryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(queryWrapper);
        return count > 0;
    }

    /**
     * 根据专辑id+用户ID获取用户已购买声音id列表
     *
     * @param albumId
     * @return
     */
    @Override
    public List<Long> getUserPaidTrackList(Long userId, Long albumId) {
        LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidTrack::getUserId, userId);
        queryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(userPaidTrackList)) {
            //获取已购声音ID列表
            List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;


    /**
     * 处理用户购买记录
     * 1.处理声音购买记录-根据订单编号避免重复增加购买记录
     * 2.处理专辑购买记录-根据订单编号避免重复增加购买记录
     * 3.处理会员购买记录
     * -根据订单编号避免重复增加购买记录
     * -修改用户表VIP状态及失效时间
     *
     * @param userPaidRecordVo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUserPayRecord(UserPaidRecordVo userPaidRecordVo) {
        //1.处理声音购买记录
        if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(userPaidRecordVo.getItemType())) {
            //1.1 根据订单编号避免重复增加购买记录
            LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserPaidTrack::getOrderNo, userPaidRecordVo.getOrderNo());
            Long count = userPaidTrackMapper.selectCount(queryWrapper);
            if (count > 0) {
                return;
            }
            //1.2 构建声音购买记录新增
            //1.3 远程调用专辑服务获取声音信息
            TrackInfo trackInfo = albumFeignClient.getTrackInfo(userPaidRecordVo.getItemIdList().get(0)).getData();
            userPaidRecordVo.getItemIdList().forEach(trackId -> {
                UserPaidTrack userPaidTrack = new UserPaidTrack();
                userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
                userPaidTrack.setUserId(userPaidRecordVo.getUserId());
                userPaidTrack.setAlbumId(trackInfo.getAlbumId());
                userPaidTrack.setTrackId(trackId);
                userPaidTrackMapper.insert(userPaidTrack);
            });
        } else if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(userPaidRecordVo.getItemType())) {
            //2.处理专辑购买记录
            //2.1 根据订单编号避免重复增加购买记录
            LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserPaidAlbum::getOrderNo, userPaidRecordVo.getOrderNo());
            Long count = userPaidAlbumMapper.selectCount(queryWrapper);
            if (count > 0) {
                return;
            }
            //2.2 新增专辑购买记录
            userPaidRecordVo.getItemIdList().forEach(albumId -> {
                UserPaidAlbum userPaidAlbum = new UserPaidAlbum();
                userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
                userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
                userPaidAlbum.setAlbumId(albumId);
                userPaidAlbumMapper.insert(userPaidAlbum);
            });
        } else if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(userPaidRecordVo.getItemType())) {
            //3.处理会员购买记录
            //3.1-根据订单编号避免重复增加购买记录
            LambdaQueryWrapper<UserVipService> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserVipService::getOrderNo, userPaidRecordVo.getOrderNo());
            Long count = userVipServiceMapper.selectCount(queryWrapper);
            if (count > 0) {
                return;
            }
            //3.2-修改用户表VIP状态及失效时间
            Date startTime = new Date();
            //2.1.1 获取用户
            UserInfo userInfo = userInfoMapper.selectById(userPaidRecordVo.getUserId());
            //2.1.2 根据套餐ID查询VIP套餐信息
            VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(userPaidRecordVo.getItemIdList().get(0));
            Date expireTime = new Date();


            //刚购买VIP VIP开始时间-当前，过期时间：当前时间+购买月数
            //已是VIP且在有效期，过期时间：现有失效时间+购买月数
            if (userInfo.getIsVip() == 1 && userInfo.getVipExpireTime().after(new Date())) {
                expireTime = DateUtil.offsetMonth(userInfo.getVipExpireTime(), vipServiceConfig.getServiceMonth());
            } else {
                //首次购买会员-过期时间
                expireTime = DateUtil.offsetMonth(startTime, vipServiceConfig.getServiceMonth());
            }
            userInfo.setIsVip(1);
            userInfo.setVipExpireTime(expireTime);
            userInfoMapper.updateById(userInfo);

            //3.3-新增用户VIP购买记录
            for (Long vipConfigId : userPaidRecordVo.getItemIdList()) {
                UserVipService userVipService = new UserVipService();
                userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
                userVipService.setUserId(userPaidRecordVo.getUserId());
                userVipService.setStartTime(startTime);
                userVipService.setExpireTime(expireTime);
                userVipService.setIsAutoRenew(0);
                userVipService.setNextRenewTime(null);
                userVipServiceMapper.insert(userVipService);
            }

        }
    }


    /**
     * 更新Vip到期失效状态
     *
     * @return
     */
    @Override
    public void updateVipExpireStatus() {
        //1.已经过期会员
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getIsVip, "1");
        queryWrapper.lt(UserInfo::getVipExpireTime, DateUtil.beginOfDay(new Date()));
        List<UserInfo> list = this.list(queryWrapper);
        //2.批量更新
        if (CollectionUtil.isNotEmpty(list)) {
            List<UserInfo> collect = list.stream().map(userInfo -> {
                UserInfo userInfo1 = new UserInfo();
                userInfo1.setIsVip(0);
                userInfo1.setId(userInfo.getId());
                return userInfo1;
            }).collect(Collectors.toList());
            this.updateBatchById(collect);
        }
    }


}
